#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/user.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>

void outp(uint port, uint value)
{
	printf("outp(%d,%d)\n", port,value);
}

uint inp(uint port)
{
	uint value = 0;
	printf("inp (%d)\n", port,value);
	return value;
}

int main(int argc, char** argv)
{
	if(argc!=2) {
        printf("usage: %s [executable]\n", argv[0]);
        return -2;
    }

    pid_t child;
    long orig_eax;
    child = fork();
    if(child == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        printf("Running %s\n", argv[1]);
        execl(argv[1], argv[1], NULL);
    }
    else {
        while(true){
            int wait_status;
            wait(&wait_status);
            if(WIFEXITED(wait_status)){
                printf("child exited\n");
                return 0;
            }
                
            if(!WIFSTOPPED(wait_status))
                continue;
           
            if ( !(WSTOPSIG(wait_status) == SIGSEGV ) )
            {
                printf("unhandled signal %s\n", strsignal(WSTOPSIG(wait_status)));
                if(ptrace(PTRACE_CONT, child, 0, 0) < 0) {
                    printf("ptrace\n");
                    return -1;
                }
                continue;
            }
            unsigned char* opcode_bytes;
            unsigned char imm;
            unsigned int eax;
			unsigned char al;
            unsigned int edx;
            unsigned short dx;
            unsigned opcode;
            struct user_regs_struct regs;
            ptrace(PTRACE_GETREGS, child, 0, &regs);

            opcode = ptrace(PTRACE_PEEKTEXT, child, regs.eip, 0);
            opcode_bytes = (unsigned char*)&opcode;

            switch(opcode_bytes[0]){
                case 0xE7:
                    // outl eax,imm8  
                    imm = opcode_bytes[1];
                    eax = regs.eax;
                    printf("outl eax(0x%02X),imm8(0x%02X)\n", eax,imm);    
					outp(imm, eax);
                    regs.eip += 2;
                    break;
				case 0xE6:
					//outb al,imm8
					imm = opcode_bytes[1];
					eax = regs.eax;
					al = (unsigned char)eax;
                    printf("outb al(0x%02X),imm8(0x%02X)\n", al,imm);
					outp(imm, al);
                    regs.eip += 2;
                    break;
                case 0xEE:
                    // outb al,dx
                    eax = regs.eax;
					al = (unsigned char)eax;
                    edx = regs.edx;
                    dx = edx & 0xffff;
                    printf("outb al(0x%02X),dx(0x%04X)\n", al, dx);          
					outp(dx, al);
                    regs.eip += 1;
                    break;
                case 0xEF:
                    // outl eax,dx
                    eax = regs.eax;
                    edx = regs.edx;
                    dx = edx & 0xffff;
                    printf("outl eax(0x%02X),dx(0x%04X)\n", eax, dx);          
					outp(dx, eax);
                    regs.eip += 1;
                    break;
                case 0xE5:
                    // inl imm8,eax
                    imm = opcode_bytes[1];
                    eax = regs.eax;
                    printf("inl imm(0x%02X),eax(0x%02X)\n", imm,eax);              
					regs.eax = inp(imm);
                    regs.eip += 2;
                    break;
				case 0xE4:
					//inb imm8,al
					imm = opcode_bytes[1];
					eax = regs.eax;
					al = (unsigned char)eax;
                    printf("inb imm(0x%02X),al(0x%02X)\n", imm,al);
					regs.eax = regs.eax & 0xffffff00 + inp(imm) & 0xff;
                    regs.eip += 2;
                    break;
                case 0xEC:
                    // inb dx, al
                    eax = regs.eax;
					al = (unsigned char)eax;
                    edx = regs.edx;
                    dx = edx & 0xffff;
                    printf("inb dx(0x%02X),al(0x%04X)\n", dx, al);     
					regs.eax = regs.eax & 0xffffff00 + inp(dx) & 0xff;     
                    regs.eip += 1;
                    break;
                case 0xED:
                    // inl dx, eax
                    eax = regs.eax;
                    edx = regs.edx;
                    dx = edx & 0xffff;
                    printf("inl dx(0x%02X),eax(0x%04X)\n", dx, eax);
					regs.eax = inp(dx);        
                    regs.eip += 1;
                    break;
                default:
                    printf("unhandled opcode 0x%02X\n", opcode_bytes[0]);
                    return -1;
            };            
            ptrace(PTRACE_SETREGS, child, 0, &regs);
            ptrace(PTRACE_CONT, child, 0, 0);
        }      
    }

    return 0;
}

